package prom

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/patrickmn/go-cache"
	"github.com/prometheus/common/promlog"
	"io/ioutil"
	"math"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
	"ubi-helper/src/models"
)

var client = http.Client{Timeout: 60 * time.Second}

var logger = promlog.New(&promlog.Config{})

//var lcache = cache.New(20*time.Second, 30*time.Second)

const (
	promServerUrl     = "http://117.50.35.145:9090"
	promQueryApi      = "/api/v1/query"
	promQueryRangeApi = "/api/v1/query_range"
	nodeExporterPort  = ":9100"
)

func getWithBind(bind interface{}, url string) error {
	resp, err := client.Get(url)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf(" http Status code is %d ", resp.StatusCode)
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return json.Unmarshal(data, bind)
}

func getWithString(ret *string, url string) error {
	resp, err := client.Get(url)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf(" http Status code is %d ", resp.StatusCode)
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	*ret = string(data)

	return nil
}

func parseDurationString(ds string) (time.Duration, error) {
	x := ds[len(ds)-1]
	n, err := strconv.Atoi(ds[:len(ds)-1])
	if err != nil {
		return 0, err
	}
	switch x {
	case 'm':
		return time.Duration(n) * time.Minute, nil
	case 'd':
		return time.Duration(n) * 24 * time.Hour, nil
	case 's':
		return time.Duration(n) * time.Second, nil
	case 'w':
		return time.Duration(n) * 7 * 24 * time.Hour, nil
	case 'h':
		return time.Duration(n) * time.Hour, nil
	case 'y':
		return time.Duration(n) * 365 * 24 * time.Hour, nil
	default:
		return 0, fmt.Errorf("invalid unit - %v", x)
	}

}

type PrometheusResponse struct {
	Status string                 `json:"status"`
	Data   *models.PrometheusData `json:"data"`
}

func calculateStep(duration time.Duration) int32 {
	return int32(math.Max(math.Floor(float64(duration/time.Second)/250), 1))
}

func createUrl(params ...string) string {

	if len(params) == 0 {
		return ""
	}

	var buf bytes.Buffer

	for _, param := range params {
		buf.WriteString(param)
	}

	return buf.String()
}

// 获取历史指标信息
func queryHistoryMetrics(duration string, promString string) (*PrometheusResponse, error) {
	var (
		queryString string
		start, end  int64
	)

	dur, err := parseDurationString(duration)
	if err != nil {
		logger.Log("parse Duration err", err)
		return nil, err
	}

	step := calculateStep(dur)
	now := time.Now()
	end = now.Unix()
	start = now.Add(-dur).Unix()

	fmt.Println(promString)
	queryString = fmt.Sprintf("?query=%s&start=%d&end=%d&step=%d", url.QueryEscape(promString), start, end, step)

	queryUrl := createUrl(promServerUrl, promQueryRangeApi, queryString)

	fmt.Println(queryUrl)

	promResp := &PrometheusResponse{}
	if err := getWithBind(&promResp, queryUrl); err != nil {
		return nil, err
	}

	fmt.Println(promResp)

	return promResp, nil
}

// 获取实时指标信息
func queryRealTimeMetrics(primaryIp string, localCache cache.Cache) (map[string]string, error) {

	var promResp string

	queryUrl := createUrl("http:", "//", primaryIp, nodeExporterPort, "/metrics")
	fmt.Printf(queryUrl)

	if err := getWithString(&promResp, queryUrl); err != nil {
		return nil, err
	}

	promRespSlice := strings.Split(promResp, "\n")
	for i := 0; i < len(promRespSlice); {
		// 进行正则过滤
		re := regexp.MustCompile("#+")
		match := re.FindString(promRespSlice[i])
		if match != "" {
			promRespSlice = append(promRespSlice[:i], promRespSlice[i+1:]...)
		} else {
			i++
		}
	}
	promRespMap := make(map[string]string)
	for _, v := range promRespSlice {
		if v != "" {
			metrics := strings.Split(v, " ")
			promRespMap[metrics[0]] = metrics[1]
		}
	}
	// 输出map
	for value := range promRespMap {
		fmt.Println(value, "结果为", promRespMap[value])
	}
	// 放入缓存
	localCache.Set("realTime", promRespMap, cache.DefaultExpiration)

	//fmt.Printf(promResp)
	return promRespMap, nil
}

// 获取

func CpuUtilization(instance string, duration string, realTime bool, wg *sync.WaitGroup, resp *models.PerformanceResponse) {
	defer wg.Done()

	if realTime {
		//promRespMap, _ := queryRealTimeMetrics(instance)

		// 本次指标值
		//current := promResp
	} else {
		// CPU的使用率 = 1 - (所有空闲状态CPU使用时间总和 )/(所有状态CPU时间总和)
		promString := fmt.Sprintf(
			"(1 - sum(increase(node_cpu_seconds_total{mode='idle',instance=~'%s.*'}[%s])) by (instance) / sum(increase(node_cpu_seconds_total{instance=~'%s.*'}[%s])) by (instance)) * 100",
			instance,
			duration,
			instance,
			duration)
		promResp, err := queryHistoryMetrics(duration, promString)
		if err != nil {
			return
		}
		resp.CPU = append(resp.CPU, &models.PerformanceInstanceItem{
			Instance: instance,
			Data:     promResp.Data,
		})
	}
}
